package org.example.maven.aspect;

import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.OperatingSystem;
import eu.bitwalker.useragentutils.UserAgent;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.example.maven.annotation.logAnnotation;
import org.example.maven.pojo.Result;
import org.example.maven.pojo.operation.OperationLog;
import org.example.maven.pojo.operation.loginLog.AddLoginLog;
import org.example.maven.pojo.user.Login.LoginRequest;
import org.example.maven.service.LoginLogService;
import org.example.maven.service.OperateService;
import org.example.maven.service.UserService;
import org.example.maven.utils.JwtUtils;
import org.example.maven.utils.SearchIpUtils;
import org.example.maven.utils.SpelUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Aspect
@Component
public class OperateLogAspect {
    @Autowired
    private OperateService operateService;
    @Autowired
    private LoginLogService loginLogService;
    @Autowired
    private UserService userService;
    @Autowired
    private HttpServletRequest request;  // 获取请求信息
    private long methodDoStartTime;//执行时间
    private final SpelExpressionParser parser = new SpelExpressionParser();

    //登录功能切点
    @Pointcut("execution(* org.example.maven.controller.UserController.login(..))")
    public void sysLoginLogPointCut(){}

    // 描述一个切点 - 只要调用了@MySysLog注解的方法都要进行日志记录
    @Pointcut("@annotation(org.example.maven.annotation.logAnnotation)")
    public void sysLogPointCut(){}

    // 通知- 前置通知： 记录方法被调用的时间，方法执行时间计时器开始
    @Before("sysLogPointCut()")
    public void beforeMethod(JoinPoint point){
        this.methodDoStartTime = System.currentTimeMillis();
    }

    // 通知- 返回后通知 - 方法调用成功
    @AfterReturning(value = "sysLogPointCut()", returning = "result")
    public void afterReturn(JoinPoint joinPoint,Object result){

        // 获取当前时间和开始时间比较 得到方法的执行时间
        long doMethodTime = System.currentTimeMillis() - this.methodDoStartTime;

        // 编写正常日志记录
        OperationLog myLog = new OperationLog();

        try{
            myLog.setDoTime((int) doMethodTime); // 方法执行时间

            //获取IP地址
            myLog.setIpAddress(getClientIp());

            //获取用户id
            myLog.setUserId(getUserId());

            //方法执行时间
            myLog.setOperateTime(new Date(this.methodDoStartTime));

            // 获取aop拦截的方法 - class字节码对象
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod(); // aop拦截的方法字节码对象

            // 获取当前方法头部的@MySysLog注解
            logAnnotation annotation = method.getAnnotation(logAnnotation.class);
            if(annotation!=null){
                myLog.setOperateType(annotation.type());
                myLog.setOperateDetail(parseDetail(joinPoint,annotation.detail()));
            }
            myLog.setOperateResult(1);
            operateService.addOperateLog(myLog);

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Before(value = "sysLoginLogPointCut()")
    public void BeforeLogin(){
        this.methodDoStartTime = System.currentTimeMillis();
    }

    //获取登录通知记录
    @AfterReturning(value = "sysLoginLogPointCut()",returning = "result")
    public void afterUserLogin(JoinPoint joinPoint,Object result){
        AddLoginLog addLoginLog=new AddLoginLog();
        addLoginLog.setLoginAddress(SearchIpUtils.getUserAddress(getClientIp()));//登录用户的地址
        addLoginLog.setLoginIp(getClientIp());//用户登录的ip地址
        Instant instant = Instant.ofEpochMilli(methodDoStartTime);
        LocalDateTime loginTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        addLoginLog.setLoginTime(loginTime);//获取用户的登录时间
        addLoginLog.setLoginSystem(parseOperatingSystem(getUserAgent(request)));//获取用户的操作系统
        addLoginLog.setUserAgent(parseBrowser(getUserAgent(request)));//获取用户使用的浏览器
        Result res= (Result) result;
        if(res.get("code").equals(200)){
            addLoginLog.setStatus(1);
            addLoginLog.setUserId(getLoginUserId((String) res.get("data")));
            addLoginLog.setLoginMessage("登录成功");
        }else {
            if(res.get("message").equals("验证码错误")||res.get("message").equals("账号或者密码错误")){
                LoginRequest loginRequest= (LoginRequest) joinPoint.getArgs()[0];
                addLoginLog.setUserId(userService.getIdByUserName(loginRequest.getUsername()));
                addLoginLog.setLoginMessage((String) res.get("message"));
                addLoginLog.setStatus(0);
            }
            else {
                addLoginLog=null;
            }
        }
        System.out.println(addLoginLog);
        if(addLoginLog!=null){
            loginLogService.addLoginLog(addLoginLog);
        }
    }

    // 通知 - 异常后通知 - 错误日志记录
    @AfterThrowing(value = "sysLogPointCut()", throwing = "e")
    public void afterException(JoinPoint joinPoint, Throwable e){
        // 获取当前时间和开始时间比较 得到方法的执行时间
        long doMethodTime = System.currentTimeMillis() - this.methodDoStartTime;
        OperationLog myLog=new OperationLog();
        try {
            myLog.setDoTime((int) doMethodTime); // 方法执行时间

            //获取IP地址
            myLog.setIpAddress(getClientIp());

            //获取用户id
            myLog.setUserId(getUserId());

            //方法开始执行时间
            myLog.setOperateTime(new Date(this.methodDoStartTime));

            // 获取aop拦截的方法 - class字节码对象
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod(); // aop拦截的方法字节码对象

            // 获取当前方法头部的@MySysLog注解
            logAnnotation annotation = method.getAnnotation(logAnnotation.class);
            if(annotation!=null){
                myLog.setOperateType(annotation.type());
                myLog.setOperateDetail(parseDetail(joinPoint,annotation.detail()));
            }
            myLog.setOperateResult(0);
            operateService.addOperateLog(myLog);
        }catch (Exception e2){
            e2.printStackTrace();
        }
    }

    // 获取客户端IP
    private String getClientIp() {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
    //获取用户id
    private Integer getUserId(){
        String token=request.getHeader("Authorization");
        return JwtUtils.getJwtUserId(token);
    }

    //用户登录时获取id
    private Integer getLoginUserId(String token){
        return JwtUtils.getJwtUserId(token);
    }

    // 解析详情中的动态参数（示例：{username}替换为实际值）
    private String parseDetail(JoinPoint pjp, String detailTemplate) {
        Matcher matcher = Pattern.compile("\\{(.*?)}").matcher(detailTemplate);

        int lastIndex = 0;
        StringBuilder result = new StringBuilder();
        String expression="";
        while (matcher.find()){
            result.append(detailTemplate, lastIndex, matcher.start());
            expression=matcher.group(1);
            result.append(SpelUtil.generateKeyBySpEL(expression,pjp));
            lastIndex = matcher.end();
        }
        result.append(detailTemplate.substring(lastIndex));
        return result.toString();
    }


    //获取user-agent信息
    public String getUserAgent(HttpServletRequest request) {
        return request.getHeader("User-Agent");
    }

    //获取用户使用的操作系统
    public String parseOperatingSystem(String userAgent) {
        UserAgent ua = UserAgent.parseUserAgentString(userAgent);
        OperatingSystem os = ua.getOperatingSystem();
        return os.getName(); // 例如 "Windows 10", "Mac OS X"
    }
    //获取用户使用的浏览器
    public String parseBrowser(String userAgent){
        UserAgent ua = UserAgent.parseUserAgentString(userAgent);
        Browser os = ua.getBrowser();
        return os.getName(); // 例如 "Chrome"
    }
}
